﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;
using Microsoft.SharePoint.Taxonomy;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebPartPages;

namespace MediawikiSPMigratorClass
{
    public class SharePointData
    {
        public SPList CurrentList;
        public SPWeb CurrentWeb;
        public SPDocumentLibrary CurrentLibrary;
        public SPFolder CurrentRootFolder;
        public PublishingWeb CurrentPublishingWeb;
        public SPSite CurrentSite;
        Dictionary<string, Dictionary<string, string>> CurrentTemplateDetails;

        public SharePointData(SPList List, Dictionary<string, Dictionary<string, string>> TemplateDetails)
        {
            CurrentList = List;
            CurrentWeb = CurrentList.ParentWeb;
            CurrentPublishingWeb = PublishingWeb.GetPublishingWeb(CurrentWeb);
            CurrentRootFolder = CurrentList.RootFolder;
            CurrentLibrary = CurrentList as SPDocumentLibrary;
            CurrentSite = CurrentWeb.Site;
            CurrentTemplateDetails = TemplateDetails;
        }

        

        public List<string> GetPageTemplates()
        {
            List<string> pageLayouts = new List<string>();

            foreach (SPContentType contentType in CurrentLibrary.ContentTypes)
            {
                if (CurrentTemplateDetails != null && CurrentTemplateDetails.ContainsKey(contentType.Name))
                {
                    pageLayouts.Add(contentType.Name);
                }
            }

            if (pageLayouts.Count == 0)
            { pageLayouts.Add("Basic Page"); }
            
            return pageLayouts;
        }

        public void CreatePage(string PageFileName, string WikiText)
        {
            CurrentWeb.AllowUnsafeUpdates = true;
            try
            {
                
                List<string> pageTemplates = GetPageTemplates();
                //use the first available template as the page template to use
                string layoutname = pageTemplates[0];
                PageLayout layout = GetSelectedPageLayout(pageTemplates);
                string serverRelativeUrl = CurrentWeb.ServerRelativeUrl + "/" + CurrentList.Title + "/" + PageFileName + ".aspx";
                LogData.WriteToLog("Page title: " + serverRelativeUrl);
                PublishingPage selectedPage = CurrentPublishingWeb.GetPublishingPage(serverRelativeUrl);
                if (selectedPage == null)
                { selectedPage = CurrentPublishingWeb.GetPublishingPages().Add(serverRelativeUrl, layout); }
                selectedPage.Title = PageFileName;
                selectedPage.ListItem["PublishingPageContent"] = WikiText;
//                selectedPage.Update();
                try
                {
                    MappingData mapping = new MappingData();
                    XElement mappingDetails = mapping.GetMappingDetails(layoutname);
                    LogData.WriteToLog("Running mapping on " + serverRelativeUrl + " with layout " + layoutname);
                    ProcessFieldsFromTemplateMapping(mappingDetails, selectedPage);
                    LogData.WriteToLog("Processed fields for " + serverRelativeUrl);
                }
                catch (Exception exp)
                {
                    LogData.WriteToLog("There was an error processing fields: " + exp.Message + "; Stacktrace: " + exp.StackTrace);
                    throw exp;
                }
                finally
                {
                    selectedPage.Update();
                    LogData.WriteToLog("Page " + serverRelativeUrl + " updated");
                    SPFile pageFile = selectedPage.ListItem.File;

                    if (pageFile.CheckOutStatus != SPFile.SPCheckOutStatus.None)
                    {

                        pageFile.CheckIn("CheckedIn");
                        LogData.WriteToLog("Page " + serverRelativeUrl + " checked in");
                        pageFile.Publish("publihsed");
                        LogData.WriteToLog("Page " + serverRelativeUrl + " published");
                    }
                }
                /*
                using (SPSite site = new SPSite(SPSiteText.Text))
                {
                    TaxonomyField usedByField = list.Fields["Used By"] as TaxonomyField;
                    TaxonomySession session = new TaxonomySession(site);
                    //TODO: Make this more flexible. Field name must match termset name. TermStore name defined in config file.
                    TermStore store = session.TermStores[0];
                    TermSetCollection termsets = store.GetTermSets("Used By", 1033);
                    TermSet termset = termsets[0];
                    TermCollection terms = termset.GetTerms("London", false);
                    Term term = terms[0];
                    usedByField.SetFieldValue(SelectedPage.ListItem, term);

                }
                
                 */
            }
            finally
            {
                CurrentWeb.AllowUnsafeUpdates = false;
            }
        }

        public PageLayout GetSelectedPageLayout(List<string> PageTemplates)
        {
            PageLayout[] layouts = CurrentPublishingWeb.GetAvailablePageLayouts();

            PageLayout layout = null;
            string layoutname = string.Empty;
            foreach (PageLayout selectedLayout in layouts)
            {
                if (selectedLayout.Title == PageTemplates[0])
                {
                    layout = selectedLayout;
                    layoutname = selectedLayout.Title;
                }
            }

            return layout;
        }

        public void ProcessFieldsFromTemplateMapping(XElement Template, PublishingPage SelectedPage)
        {
            List<string> errors = new List<string>();
            string templateType = Template.Attribute("Type").Value;
            foreach(XElement field in Template.Descendants("Field"))
            {
                try
                {
                    string fieldType = field.Attribute("Type").Value;
                    string fieldDestination = "Unknown";
                    if (field.Attribute("Destination") != null)
                    { fieldDestination = field.Attribute("Destination").Value; }
                    switch (fieldType.ToLower())
                    {
                        case "text":
                            LogData.WriteToLog("Processing string field " + fieldDestination);
                            ProcessStringField(field, SelectedPage, templateType);
                            break;
                        case "metadata":
                            LogData.WriteToLog("Processing metadata field " + fieldDestination);
                            ProcessMetadataField(field, SelectedPage, templateType);
                            break;
                        case "people":
                            LogData.WriteToLog("Processing people field " + fieldDestination);
                            ProcessPeopleField(field, SelectedPage, templateType);
                            break;
                        default:
                            LogData.WriteToLog("Unable to determine type for " + fieldDestination);
                            errors.Add("Unable to determine type: " + field.ToString());
                            break;
                    }
                }
                catch (Exception exp)
                { errors.Add(exp.Message + ";\n\nStacktrace: " + exp.StackTrace); }
            }
            if (errors.Count() > 0)
            {
                string errorText = string.Empty;
                foreach(string error in errors)
                {
                    errorText += error + "\n";
                }
                throw new Exception("There was an error or errors processing the page from the mapping: \n" + errorText);
            }
        }

        public void ProcessStringField(XElement Field, PublishingPage SelectedPage, string TemplateType)
        {
            string source = Field.Attribute("Source").Value;
            string destination = GetDestinationAttribute(Field);
            
            if (CurrentTemplateDetails[TemplateType].ContainsKey(source))
            { 
                SelectedPage.ListItem[destination] = CurrentTemplateDetails[TemplateType][source];
            }
        }

        public void ProcessMetadataField(XElement Field, PublishingPage SelectedPage, string TemplateType)
        {
            if (Field.Attribute("Grouped") != null && Field.Attribute("Grouped").Value == "Yes")
            {
                ProcessGroupedMetadataField(Field, SelectedPage, TemplateType);
                return;
            }
            if (Field.Attribute("Mapped") != null && Field.Attribute("Mapped").Value == "Yes")
            {
                ProcessMappedMetadataField(Field, SelectedPage, TemplateType);
                return;
            }
            string destination = GetDestinationAttribute(Field);
            string source = GetSourceAttribute(Field);
            string metadataFieldText = GetMetadataFieldAttribute(Field);
            if (!CurrentList.Fields.ContainsField(destination))
            { throw new Exception("Unable to find the field " + destination + " in the Page Properties for SharePoint. Please check the available content types."); }

            TaxonomyField metadataField = CurrentList.Fields[destination] as TaxonomyField;
            TaxonomySession session = new TaxonomySession(CurrentSite);
            //TODO: Make this more flexible. Field name must match termset name. TermStore name defined in config file.
            TermStore store = session.TermStores[0];
            TermSetCollection termsets = store.GetTermSets(metadataFieldText, 1033);
            if (termsets.Count == 0)
            {
                Group group = store.CreateGroup(metadataFieldText);
                store.CommitAll();
                termsets = store.GetTermSets(metadataFieldText, 1033);

                //throw new KeyNotFoundException("Could not find metadata group " + metadataFieldText + ": " + Field.ToString()); }
            }
            TermSet termset = termsets[0];
            string termToFind = CurrentTemplateDetails[TemplateType][source];
            TermCollection terms = termset.GetTerms(termToFind, false);
            
            if (terms.Count == 0)
            {
                Term newTerm = termset.CreateTerm(termToFind, 1033);
                store.CommitAll();
                terms = termset.GetTerms(termToFind, false);
                //throw new KeyNotFoundException("Could not find metadata field value " + termToFind + ": " + Field.ToString()); }
            }
            Term term = terms[0];
            metadataField.SspId = store.Id;
            metadataField.Update();
            metadataField.SetFieldValue(SelectedPage.ListItem, term, 1033);
        }

        public void ProcessGroupedMetadataField(XElement Field, PublishingPage SelectedPage, string TemplateType)
        {
            string destination = GetDestinationAttribute(Field);
            string metadataFieldText = GetMetadataFieldAttribute(Field);

            TaxonomyField metadataField = CurrentList.Fields[destination] as TaxonomyField;
            TaxonomySession session = new TaxonomySession(CurrentSite);
            //TODO: Make this more flexible. Field name must match termset name. TermStore name defined in config file.
            TermStore store = session.TermStores[0];
            TermSetCollection termsets = store.GetTermSets(metadataFieldText, 1033);
            if (termsets.Count == 0)
            { throw new KeyNotFoundException("Could not find metadata group " + metadataFieldText + ": " + Field.ToString()); }
            TermSet termset = termsets[0];
            List<Term> termsToInclude = new List<Term>();
            
            List<string> errors = new List<string>();
            foreach (XElement source in Field.Descendants("Source"))
            {
                if (source.Attribute("SourceText") == null)
                { errors.Add("SourceText has not been defined on the source item: " + source.ToString()); }
                else
                {
                    string sourceText = source.Attribute("SourceText").Value;
                    if (CurrentTemplateDetails[TemplateType][sourceText] != null && CurrentTemplateDetails[TemplateType][sourceText] == "yes")
                    {
                        string termToFind = source.Value;
                        TermCollection terms = termset.GetTerms(termToFind, false);
                        if (terms.Count == 0)
                        { errors.Add("Could not find metadata field value " + termToFind + ": " + Field.ToString()); }
                        else
                        { termsToInclude.Add(terms[0]); }
                    }
                }
            }
            if (errors.Count > 0)
            { 
                string errorText = string.Empty;
                foreach(string error in errors)
                { errorText += error + "\n"; }
                throw new KeyNotFoundException("Could not find some values: " + errorText);
            }
            if (termsToInclude.Count() > 0)
            {
                metadataField.SetFieldValue(SelectedPage.ListItem, termsToInclude);
            }
        }

        public void ProcessMappedMetadataField(XElement Field, PublishingPage SelectedPage, string TemplateType)
        {
            string destination = GetDestinationAttribute(Field);
            string metadataFieldText = GetMetadataFieldAttribute(Field);
            string source = GetSourceAttribute(Field);

            TaxonomyField metadataField = CurrentList.Fields[destination] as TaxonomyField;
            TaxonomySession session = new TaxonomySession(CurrentSite);
            //TODO: Make this more flexible. Field name must match termset name. TermStore name defined in config file.
            TermStore store = session.TermStores[0];
            TermSetCollection termsets = store.GetTermSets(metadataFieldText, 1033);
            if (termsets.Count == 0)
            { throw new KeyNotFoundException("Could not find metadata group " + metadataFieldText + ": " + Field.ToString()); }
            TermSet termset = termsets[0];
            if (CurrentTemplateDetails[TemplateType][source] != null)
            {
                string mappingSource = CurrentTemplateDetails[TemplateType][source];
                var result = from t in Field.Descendants("Mapping")
                             where ((XElement)t).Attribute("Source").Value == mappingSource
                             select t;
                string termToFind = result.First().Value;


                TermCollection terms = termset.GetTerms(termToFind, false);
                if (terms.Count == 0)
                { throw new KeyNotFoundException("Could not find metadata field value " + termToFind + ": " + Field.ToString()); }
                else
                { metadataField.SetFieldValue(SelectedPage.ListItem, terms[0]); }
            }
        }

        public void ProcessPeopleField(XElement Field, PublishingPage SelectedPage, string TemplateType)
        {
            string source = Field.Attribute("Source").Value;
            string destination = GetDestinationAttribute(Field);

            if (CurrentTemplateDetails[TemplateType].ContainsKey(source))
            {
                //[http://icevbcs.london.schroders.com/gm/user-1.59.9177 Nick Truyens]
                //[http://icevbs.london.schroders.com/portal/site/myportal/vca/user/profile Matthew Lees]
                SelectedPage.ListItem[destination] = CurrentTemplateDetails[TemplateType][source];
            }
        }

        public void ProcessImageField(XElement Field, PublishingPage SelectedPage, string TemplateType)
        {
            throw new NotImplementedException();
        }
        
        private string GetDestinationAttribute(XElement Field)
        {
            if (Field.Attribute("Destination") == null)
            { throw new Exception("Destination has not been set in the field: " + Field.ToString()); }
            return Field.Attribute("Destination").Value;
        }

        private string GetSourceAttribute(XElement Field)
        {
            if (Field.Attribute("Source") == null)
            { throw new Exception("Source has not been set in the field: " + Field.ToString()); }
            return Field.Attribute("Source").Value;
        }

        private string GetMetadataFieldAttribute(XElement Field)
        {
            if (Field.Attribute("MetadataField") == null)
            { throw new Exception("MetadataField has not been set in the field: " + Field.ToString()); }
            return Field.Attribute("MetadataField").Value;
        }

    }
}
